package com.moon.cloud.log.aspect;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.alibaba.fastjson2.JSON;
import com.moon.cloud.log.annotation.LogController;
import com.moon.cloud.log.annotation.LogMethod;
import com.moon.cloud.log.config.LogProperties;
import com.moon.cloud.log.entity.RequestLog;
import com.moon.cloud.log.mapper.RequestLogMapper;
import com.moon.cloud.log.util.LogConstants;
import com.moon.cloud.log.util.LogUtils;

import jakarta.servlet.http.HttpServletRequest;

@Aspect
public class LogAspect {
	private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);

	@Autowired
	private LogProperties properties;
	private RequestLogMapper logMapper;
	private String serverName;

	// 存储每个线程的请求开始时间
	private final ThreadLocal<LocalDateTime> startTimeThreadLocal = new ThreadLocal<>();

	// 定义切点，支持@Controller和@RestController
	@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping) || " +
			"@annotation(org.springframework.web.bind.annotation.GetMapping) || " +
			"@annotation(org.springframework.web.bind.annotation.PostMapping) || " +
			"@annotation(org.springframework.web.bind.annotation.PutMapping) || " +
			"@annotation(org.springframework.web.bind.annotation.DeleteMapping) || " +
			"@annotation(org.springframework.web.bind.annotation.PatchMapping)")
	public void webRequest() {
	}

	@Before("webRequest()")
	public void before(JoinPoint joinPoint) {
		if (!properties.isEnabled())
			return;

		// 检查请求路径，如果是 /error 则不记录日志
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		// 检查请求路径，如果是 /error 则不记录日志
		if (this.checkError(attributes)) {
			return;
		}

		startTimeThreadLocal.set(LocalDateTime.now());

		// 记录请求信息
		if (attributes != null) {
			HttpServletRequest request = attributes.getRequest();
			logger.debug("Request intercepted: {}", request.getRequestURI());
		}
	}

	@AfterReturning(pointcut = "webRequest()", returning = "result")
	public void afterReturning(JoinPoint joinPoint, Object result) {
		if (!properties.isEnabled())
			return;
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		// 检查请求路径，如果是 /error 则不记录日志
		if (this.checkError(attributes)) {
			return;
		}

		try {
			RequestLog log = buildRequestLog(joinPoint, null);
			log.setRequestStatus(LogConstants.RequestStatus.SUCCESS);
			if (properties.isIncludeResult()) {
				log.setRequestResult(result != null ? result.toString() : "null");
			}
			log.setRequestEndTime(LocalDateTime.now());
			logMapper.insert(log);
		} catch (Exception e) {
			logger.error("Failed to save request log", e);
		} finally {
			startTimeThreadLocal.remove();
		}
	}

	@AfterThrowing(pointcut = "webRequest()", throwing = "ex")
	public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
		if (!properties.isEnabled())
			return;

		// 检查请求路径，如果是 /error 则不记录日志
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		// 检查请求路径，如果是 /error 则不记录日志
		if (this.checkError(attributes)) {
			return;
		}

		try {
			RequestLog log = buildRequestLog(joinPoint, ex);
			log.setRequestStatus(LogConstants.RequestStatus.ERROR);
			if (properties.isIncludeException()) {
				log.setRequestException(ex.getMessage());
			}
			log.setRequestEndTime(LocalDateTime.now());
			logMapper.insert(log);
		} catch (Exception e) {
			logger.error("Failed to save exception log", e);
		} finally {
			startTimeThreadLocal.remove();
		}
	}

	private RequestLog buildRequestLog(JoinPoint joinPoint, Throwable ex) {
		RequestLog log = new RequestLog();
		log.setServerName(serverName);
		log.setRequestTime(startTimeThreadLocal.get());
		// 获取请求信息
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		if (attributes != null) {
			HttpServletRequest request = attributes.getRequest();
			String requestUri = request.getRequestURI();
			log.setRequestUrl(requestUri);
			log.setRequestMethod(request.getMethod());

			// 设置请求IP
			String clientIp = LogUtils.getClientIpAddress(request);
			log.setRequestIp(clientIp);

			// 设置请求头信息
			if (properties.isIncludeHeaders()) {
				java.util.Enumeration<String> headerNames = request.getHeaderNames();
				HashMap<String, String> headers = new HashMap<>();
				while (headerNames.hasMoreElements()) {
					String headerName = headerNames.nextElement();
					String headerValue = request.getHeader(headerName);
					headers.put(headerName, headerValue);
				}
				log.setRequestHeaders(JSON.toJSONString(headers));
			}

			// 获取控制器和方法上的注解
			Class<?> targetClass = joinPoint.getTarget().getClass();
			LogController logController = AnnotationUtils.findAnnotation(targetClass, LogController.class);
			if (logController != null) {
				log.setRequestController(logController.value());
			}

			org.aspectj.lang.reflect.MethodSignature signature = (org.aspectj.lang.reflect.MethodSignature) joinPoint
					.getSignature();
			Method method = signature.getMethod();
			LogMethod logMethod = AnnotationUtils.findAnnotation(method, LogMethod.class);
			if (logMethod != null) {
				log.setRequestOperate(logMethod.value());
			}

			// 处理请求参数
			Map<String, Object> paramMap = new HashMap<>();
			request.getParameterMap().forEach((k, v) -> {
				paramMap.put(k, v.length == 1 ? v[0] : v);
			});

			// 处理方法参数
			Object[] args = joinPoint.getArgs();
			if (args != null && args.length > 0) {
				String[] parameterNames = signature.getParameterNames();

				for (int i = 0; i < args.length; i++) {
					if (parameterNames != null && i < parameterNames.length) {
						// 处理文件参数
						if (LogUtils.isFileParameter(args[i])) {
							paramMap.put(parameterNames[i], "文件");
						} else {
							paramMap.put(parameterNames[i], args[i]);
						}
					}
				}
			}

			log.setRequestParam(paramMap.toString());
		}

		return log;
	}

	public boolean checkError(ServletRequestAttributes requestAttributes) {
		// 检查请求路径，如果是 /error 则不记录日志
		if (requestAttributes != null) {
			HttpServletRequest request = requestAttributes.getRequest();
			String requestUri = request.getRequestURI();
			if (LogConstants.ErrorPath.ERROR_PATH.equals(requestUri)) {
				logger.debug("Skip logging for error path: {}", requestUri);
				return true;
			}
		}
		return false;
	}

	public void setProperties(LogProperties properties) {
		this.properties = properties;
	}

	public void setLogMapper(RequestLogMapper logMapper) {
		this.logMapper = logMapper;
	}

	public void setServerName(String serverName) {
		this.serverName = serverName;
	}
}